package jcu.sal.client.gui.view;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.rmi.RemoteException;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import jcu.sal.client.gui.ClientController;
import jcu.sal.client.gui.ClientControllerImpl;
import jcu.sal.common.Constants;
import jcu.sal.common.agents.SALAgent;
import jcu.sal.common.events.Event;
import jcu.sal.common.exceptions.ConfigurationException;
import jcu.sal.common.exceptions.NotFoundException;

public class ClientViewImpl extends AbstractClientView implements
		ActionListener {

	private static String ActionAddRMIAgent = "addRMI";
	private static String ActionRemoveRMIAgent = "removeRMI";
	private static String ActionAddNettyAgent = "addNetty";
	private static String ActionRemoveNettyAgent = "removeNetty";
	private static String ActionQuit = "quit";

	private String name;
	private JMenuItem disconnectRMIAgent;
	private JMenuItem disconnectNettyAgent;

	/**
	 * This method creates a new SAL Client view
	 * 
	 * @param c
	 *            the SAL client controller managing interactions with different
	 *            SAL agents
	 * @param n
	 *            a unique name for this client
	 */
	public ClientViewImpl(ClientController c, String n) {
		super(c);
		controller = c;
		name = n;
	}

	@Override
	public void release() {
		// unregister event handlers and release all agents
		for (SALAgent a : tree.getAgents()) {
			unregisterEventhandlers(a);
			controller.disconnect(a);
		}
	}

	/**
	 * this method registers this client for all {@link Event}s generated by the
	 * given SALAgent.
	 * 
	 * @param a
	 *            the SAL agent whose {@link Event}s we want to register for
	 * @throws NotFoundException
	 *             if there is an error registering
	 */
	public void registerEventHandlers(SALAgent a) throws NotFoundException {
		controller.registerEventHandler(a, this,
				Constants.PROTOCOL_MANAGER_PRODUCER_ID);
		controller.registerEventHandler(a, this,
				Constants.SENSOR_MANAGER_PRODUCER_ID);
		controller.registerEventHandler(a, this,
				Constants.SENSOR_STATE_PRODUCER_ID);
	}

	/**
	 * This method unregisters the event handlers previously registered with
	 * {@link #registerEventHandlers(SALAgent)}.
	 * 
	 * @param a
	 *            the SAL agent who we want to unregister from
	 */
	public void unregisterEventhandlers(SALAgent a) {
		try {
			controller.unregisterEventHandler(a, this,
					Constants.PROTOCOL_MANAGER_PRODUCER_ID);
			controller.unregisterEventHandler(a, this,
					Constants.SENSOR_MANAGER_PRODUCER_ID);
			controller.unregisterEventHandler(a, this,
					Constants.SENSOR_STATE_PRODUCER_ID);
		} catch (Throwable e1) {
			e1.printStackTrace();
		}
	}

	/**
	 * This method is called by {@link SensorTree} when a component (sensor,
	 * protocol, agent) has been selected
	 * 
	 * @param c
	 *            the {@link Context} of the selected object
	 */
	@Override
	public void componentSelected(Context c) {
		disconnectRMIAgent.setEnabled(false);
		if (c.getType() == Context.ROOT_NODE)
			addLog("Root Node '" + c.toString() + "' selected");
		else if (c.getType() == Context.AGENT_TYPE) {
			if (c.getAgent().getType().equals(Constants.RMI_Agent_type))
				disconnectRMIAgent.setEnabled(true);
			else if (c.getAgent().getType().equals(Constants.Netty_Agent_type))
				disconnectNettyAgent.setEnabled(true);
			addLog("Agent Node '" + c.toString() + "' selected");
		} else if (c.getType() == Context.PROTOCOL_TYPE)
			addLog("Protocol '" + c.toString() + "' selected");
		else if (c.getType() == Context.SENSOR_TYPE)
			actionPane.displaySensor(c);
		else
			addLog("Unknown element type");

		actionPane.getPanel().revalidate();
		actionPane.getPanel().repaint();

	}

	/**
	 * This method is called when the user selected the "Connect to RMI Agent"
	 * menu item. It asks the user for the IP addresses of RMI registries,
	 * creates the client stub and updates the GUI
	 */
	public void addRmiAgent() {
		Vector<String> v;
		try {
			v = new Vector<String>();
			Enumeration<NetworkInterface> i = NetworkInterface
					.getNetworkInterfaces();
			Enumeration<InetAddress> add;
			while (i.hasMoreElements()) {
				add = i.nextElement().getInetAddresses();
				while (add.hasMoreElements())
					v.add(add.nextElement().getHostAddress());
			}

		} catch (SocketException e1) {
			JOptionPane.showMessageDialog(frame,
					"Unable to list the network interfaces.", "Network error",
					JOptionPane.ERROR_MESSAGE);
			return;
		}
		String[] ifs = new String[v.size()];
		v.copyInto(ifs);

		String agentIP = (String) JOptionPane.showInputDialog(frame,
				"Enter the IP address of the RMI SAL agent:\n",
				"SAL Agent detail", JOptionPane.QUESTION_MESSAGE, null, null,
				"127.0.0.1");
		if (agentIP == null)
			return;

		String ourIP = (String) JOptionPane.showInputDialog(frame,
				"Enter the IP address of this client's RMI registry:\n",
				"SAL Agent detail", JOptionPane.QUESTION_MESSAGE, null, ifs,
				// null,
				"127.0.0.1");

		if (ourIP != null) {
			try {
				SALAgent a = controller.rmiConnect(name, agentIP, ourIP);
				registerEventHandlers(a);
				tree.addAgent(a);
			} catch (ConfigurationException e) {
				addLog("Chosen RMI name '" + name
						+ "' already registered with agent");
				e.printStackTrace();
			} catch (RemoteException e) {
				addLog("RMI error while connecting to agent");
				e.printStackTrace();
			} catch (NotFoundException e) {
				addLog("Error registering event handlers with agent");
				e.printStackTrace();
			}

		}
	}

	public void removeRmiAgent(SALAgent a) {
		disconnectRMIAgent.setEnabled(false);
		tree.removeAgent(a);
		actionPane.displaySensor(null);
		unregisterEventhandlers(a);
		controller.rmiDisonnect(a);
	}

	/**
	 * This method is called when the user selected the "Connect to Netty Agent"
	 * menu item. It asks the user for the IP addresses of the agent, creates
	 * the client stub and updates the GUI
	 */
	public void addNettyAgent() {
		String agentIP = (String) JOptionPane.showInputDialog(frame,
				"Enter the IP address of the Netty SAL agent:\n",
				"SAL Agent detail", JOptionPane.QUESTION_MESSAGE, null, null,
				"127.0.0.1");
		if (agentIP == null)
			return;

		SALAgent a = controller.nettyConnect(name, agentIP);
		try {
			registerEventHandlers(a);
		} catch (NotFoundException e) {
			addLog("Error registering event handlers with agent");
			e.printStackTrace();
		}
		tree.addAgent(a);

	}

	public void removeNettyAgent(SALAgent a) {
		disconnectNettyAgent.setEnabled(false);
		tree.removeAgent(a);
		actionPane.displaySensor(null);
		unregisterEventhandlers(a);
		controller.nettyDisonnect(a);
	}

	/**
	 * This method is invoked by swing whenever an item in the menu bar is
	 * selected
	 */
	public void actionPerformed(ActionEvent e) {
		if (ActionAddRMIAgent.equals(e.getActionCommand())) {
			addRmiAgent();
		} else if (ActionRemoveRMIAgent.equals(e.getActionCommand())) {
			removeRmiAgent(tree.getSelectedLabel().getAgent());
		} else if (ActionAddNettyAgent.equals(e.getActionCommand())) {
			addNettyAgent();
		} else if (ActionRemoveNettyAgent.equals(e.getActionCommand())) {
			removeNettyAgent(tree.getSelectedLabel().getAgent());
		} else if (ActionQuit.equals(e.getActionCommand())) {
			// quit
			frame.dispose();
		}
	}

	/**
	 * This method creates the menu bar
	 */
	protected JMenuBar createMenu() {
		JMenuBar bar = new JMenuBar();
		JMenu menu = new JMenu("SAL Client");
		menu.setMnemonic(KeyEvent.VK_S);
		bar.add(menu);

		JMenuItem item_add_rmi = new JMenuItem("Add RMI Agent");
		item_add_rmi.setActionCommand(ActionAddRMIAgent);
		item_add_rmi.setMnemonic(KeyEvent.VK_R);
		item_add_rmi.addActionListener(this);
		menu.add(item_add_rmi);

		disconnectRMIAgent = new JMenuItem("Disconnect RMI Agent");
		disconnectRMIAgent.setActionCommand(ActionRemoveRMIAgent);
		disconnectRMIAgent.setMnemonic(KeyEvent.VK_D);
		disconnectRMIAgent.addActionListener(this);
		disconnectRMIAgent.setEnabled(false);
		menu.add(disconnectRMIAgent);

		menu.addSeparator();

		JMenuItem item_add_netty = new JMenuItem("Add Netty Agent");
		item_add_netty.setActionCommand(ActionAddNettyAgent);
		item_add_netty.setMnemonic(KeyEvent.VK_N);
		item_add_netty.addActionListener(this);
		menu.add(item_add_netty);

		disconnectNettyAgent = new JMenuItem("Disconnect Netty Agent");
		disconnectNettyAgent.setActionCommand(ActionRemoveNettyAgent);
		disconnectNettyAgent.setMnemonic(KeyEvent.VK_A);
		disconnectNettyAgent.addActionListener(this);
		disconnectNettyAgent.setEnabled(false);
		menu.add(disconnectNettyAgent);

		menu.addSeparator();

		item_add_netty = new JMenuItem("Quit");
		item_add_netty.setActionCommand(ActionQuit);
		item_add_netty.setMnemonic(KeyEvent.VK_Q);
		item_add_netty.addActionListener(this);
		menu.add(item_add_netty);

		return bar;
	}

	public static void main(final String[] args) {

		ClientController c = new ClientControllerImpl();
		final ClientViewImpl cl = new ClientViewImpl(c, args[1]);
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				cl.initGUI();
			}
		});

	}
}
